// Use #[neon::export] to export Rust functions as JavaScript functions.
// See more at: https://docs.rs/neon/latest/neon/attr.export.html

use neon::prelude::*;
use std::ptr;
use winapi::ctypes::c_void;
use winapi::shared::minwindef::DWORD;
use winapi::shared::windef::HGDIOBJ;
use winapi::um::wingdi::{
    BI_RGB, BITMAPINFO, BITMAPINFOHEADER, BitBlt, CreateCompatibleBitmap, CreateCompatibleDC,
    DIB_RGB_COLORS, DeleteDC, DeleteObject, GetDIBits, RGBQUAD, SRCCOPY, SelectObject,
};
use winapi::um::winuser::{
    GetDC, GetSystemMetrics, ReleaseDC, SM_CXSCREEN, SM_CYSCREEN, keybd_event, mouse_event,
};

fn hello(mut cx: FunctionContext) -> JsResult<JsString> {
    let name = cx.argument::<JsString>(0)?.value(&mut cx);
    Ok(cx.string(format!("hello {name}")))
}

fn keybd_event_api(mut cx: FunctionContext) -> JsResult<JsUndefined> {
    let keycode = cx.argument::<JsNumber>(0)?.value(&mut cx) as u8;
    let flags = cx.argument::<JsNumber>(1)?.value(&mut cx) as u32;

    unsafe {
        keybd_event(keycode, 0, flags, 0);
    }

    Ok(cx.undefined())
}

fn mouse_event_api(mut cx: FunctionContext) -> JsResult<JsUndefined> {
    let dw_flags = cx.argument::<JsNumber>(0)?.value(&mut cx) as u32;
    let dx = cx.argument::<JsNumber>(1)?.value(&mut cx) as u32;
    let dy = cx.argument::<JsNumber>(2)?.value(&mut cx) as u32;
    let dw_data = cx.argument::<JsNumber>(3)?.value(&mut cx) as u32;

    unsafe {
        mouse_event(dw_flags, dx, dy, dw_data, 0);
    }

    Ok(cx.undefined())
}

fn get_system_metrics(mut cx: FunctionContext) -> JsResult<JsNumber> {
    let index = cx.argument::<JsNumber>(0)?.value(&mut cx) as i32;
    
    let value = unsafe {
        GetSystemMetrics(index)
    };
    
    Ok(cx.number(value as f64))
}

fn screenshot(mut cx: FunctionContext) -> JsResult<JsObject> {
    let x = cx.argument::<JsNumber>(0)?.value(&mut cx) as i32;
    let y = cx.argument::<JsNumber>(1)?.value(&mut cx) as i32;
    let w = cx.argument::<JsNumber>(2)?.value(&mut cx) as i32;
    let h = cx.argument::<JsNumber>(3)?.value(&mut cx) as i32;

    unsafe {
        let hdc_screen = GetDC(ptr::null_mut());
        if hdc_screen.is_null() {
            return cx.throw_error("Failed to get screen DC");
        }

        let hdc_mem = CreateCompatibleDC(hdc_screen);
        if hdc_mem.is_null() {
            ReleaseDC(ptr::null_mut(), hdc_screen);
            return cx.throw_error("Failed to create compatible DC");
        }

        let h_bitmap = CreateCompatibleBitmap(hdc_screen, w, h);
        if h_bitmap.is_null() {
            DeleteDC(hdc_mem);
            ReleaseDC(ptr::null_mut(), hdc_screen);
            return cx.throw_error("Failed to create compatible bitmap");
        }

        let old_obj = SelectObject(hdc_mem, h_bitmap as HGDIOBJ);

        if BitBlt(hdc_mem, 0, 0, w, h, hdc_screen, x, y, SRCCOPY) == 0 {
            SelectObject(hdc_mem, old_obj);
            DeleteObject(h_bitmap as *mut c_void);
            DeleteDC(hdc_mem);
            ReleaseDC(ptr::null_mut(), hdc_screen);
            return cx.throw_error("Failed to copy screen to bitmap");
        }

        let mut bmi = BITMAPINFO {
            bmiHeader: BITMAPINFOHEADER {
                biSize: std::mem::size_of::<BITMAPINFOHEADER>() as DWORD,
                biWidth: w,
                biHeight: h,
                biPlanes: 1,
                biBitCount: 24,
                biCompression: BI_RGB,
                biSizeImage: 0,
                biXPelsPerMeter: 0,
                biYPelsPerMeter: 0,
                biClrUsed: 0,
                biClrImportant: 0,
            },
            bmiColors: [RGBQUAD {
                rgbBlue: 0,
                rgbGreen: 0,
                rgbRed: 0,
                rgbReserved: 0,
            }; 1],
        };

        let mut bits = vec![0u8; (w * h * 3) as usize];

        let result = GetDIBits(
            hdc_screen,
            h_bitmap,
            0,
            h as u32,
            bits.as_mut_ptr() as *mut c_void,
            &mut bmi,
            DIB_RGB_COLORS,
        );

        if result == 0 {
            SelectObject(hdc_mem, old_obj);
            DeleteObject(h_bitmap as *mut c_void);
            DeleteDC(hdc_mem);
            ReleaseDC(ptr::null_mut(), hdc_screen);
            return cx.throw_error("Failed to get bitmap bits");
        }

        // 创建返回对象
        let obj = cx.empty_object();

        // 创建Buffer存储图像数据，不使用borrow方法
        let buffer = JsBuffer::new(&mut cx, bits.len())?;

        obj.set(&mut cx, "data", buffer)?;

        // 设置宽高属性
        let width = cx.number(w as f64);
        obj.set(&mut cx, "width", width)?;

        let height = cx.number(h as f64);
        obj.set(&mut cx, "height", height)?;

        // 清理资源
        SelectObject(hdc_mem, old_obj);
        DeleteObject(h_bitmap as *mut c_void);
        DeleteDC(hdc_mem);
        ReleaseDC(ptr::null_mut(), hdc_screen);

        Ok(obj)
    }
}

#[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> {
    cx.export_function("hello", hello)?;
    cx.export_function("screenshot", screenshot)?;
    cx.export_function("keybdEvent", keybd_event_api)?;
    cx.export_function("mouseEvent", mouse_event_api)?;
    cx.export_function("getSystemMetrics", get_system_metrics)?;
    Ok(())
}
